package com.microservice.wechat.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.microservice.common.utils.RedisUtil;
import com.microservice.wechat.entity.WechatConfig;
import com.microservice.wechat.mapper.WechatConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * 微信Token管理服务
 * 支持多账号、分布式锁、自动刷新
 */
@Service
@Slf4j
public class WechatTokenService {
    
    @Autowired
    private WechatConfigMapper configMapper;
    
    @Autowired
    private RedisUtil redisUtil;
    
    @Autowired
    private RestTemplate restTemplate;
    
    private static final String TOKEN_KEY_PREFIX = "wechat:token:";
    private static final String COVER_KEY_PREFIX = "wechat:cover:";
    private static final String LOCK_KEY_PREFIX = "wechat:token:lock:";
    private static final int TOKEN_EXPIRE = 7000; // 7000秒（提前200秒刷新）
    private static final int LOCK_EXPIRE = 10; // 10秒
    private static final int MAX_RETRY = 3; // 最大重试次数
    
    /**
     * 根据 AppID 获取 Access Token（懒加载模式）
     * 
     * @param appId 微信AppID
     * @return Access Token
     */
    public String getAccessToken(String appId) {
        return getAccessToken(appId, 0);
    }
    
    /**
     * 根据 AppID 获取 Access Token（带重试）
     * 
     * @param appId 微信AppID
     * @param retryCount 当前重试次数
     * @return Access Token
     */
    private String getAccessToken(String appId, int retryCount) {
        String tokenKey = TOKEN_KEY_PREFIX + appId;
        String lockKey = LOCK_KEY_PREFIX + appId;
        
        // 1. 先从Redis获取
        Object cachedToken = redisUtil.get(tokenKey);
        if (cachedToken != null) {
            log.debug("从缓存获取Token成功: appId={}", appId);
            return cachedToken.toString();
        }
        
        // 2. Token不存在，使用分布式锁
        boolean locked = redisUtil.set(lockKey, "1", LOCK_EXPIRE);
        
        if (!locked) {
            // 没获取到锁，等待其他实例刷新
            if (retryCount < MAX_RETRY) {
                try {
                    Thread.sleep(100);
                    return getAccessToken(appId, retryCount + 1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("获取Token被中断: appId=" + appId);
                }
            } else {
                throw new RuntimeException("获取Token超时，请稍后重试: appId=" + appId);
            }
        }
        
        try {
            // 3. 双重检查（防止重复刷新）
            cachedToken = redisUtil.get(tokenKey);
            if (cachedToken != null) {
                log.debug("双重检查，Token已存在: appId={}", appId);
                return cachedToken.toString();
            }
            
            // 4. 从数据库获取配置
            WechatConfig config = getConfigByAppId(appId);
            
            // 5. 调用微信API获取Token
            String token = fetchTokenFromWechat(config.getAppId(), config.getAppSecret());
            
            // 6. 存入Redis缓存
            redisUtil.set(tokenKey, token, TOKEN_EXPIRE);

            log.info("成功获取并缓存Token: appId={}, accountName={}", appId, config.getAccountName());
            return token;
            
        } finally {
            // 7. 释放锁
            redisUtil.del(lockKey);
        }
    }
    
    /**
     * 批量刷新所有启用账号的Token
     */
    public void refreshAllTokens() {
        // 1. 查询所有启用的配置
        List<WechatConfig> configs = configMapper.selectList(
            new QueryWrapper<WechatConfig>().eq("is_enabled", 1)
        );
        
        if (configs.isEmpty()) {
            log.warn("没有启用的微信配置");
            return;
        }
        
        log.info("开始批量刷新Token，共{}个账号", configs.size());
        
        int successCount = 0;
        int failCount = 0;
        
        // 2. 并行刷新（使用Stream并行）
        for (WechatConfig config : configs) {
            try {
                refreshTokenForConfig(config);
                successCount++;
            } catch (Exception e) {
                failCount++;
                log.error("刷新Token失败: appId={}, accountName={}, error={}", 
                    config.getAppId(), config.getAccountName(), e.getMessage());
            }
        }
        
        log.info("批量刷新Token完成: 成功={}, 失败={}", successCount, failCount);
    }
    
    /**
     * 刷新单个配置的Token
     * 
     * @param config 微信配置
     */
    private void refreshTokenForConfig(WechatConfig config) {
        String tokenKey = TOKEN_KEY_PREFIX + config.getAppId();
        String coverKey = COVER_KEY_PREFIX + config.getAppId();
        String lockKey = LOCK_KEY_PREFIX + config.getAppId();
        
        // 使用分布式锁避免重复刷新
        boolean locked = redisUtil.set(lockKey, "1", LOCK_EXPIRE);
        if (!locked) {
            log.info("其他实例正在刷新，跳过: appId={}", config.getAppId());
            return;
        }
        
        try {
            String token = fetchTokenFromWechat(config.getAppId(), config.getAppSecret());
            redisUtil.set(tokenKey, token, TOKEN_EXPIRE);
            redisUtil.set(coverKey, config.getThumbMediaId(), TOKEN_EXPIRE);
            log.info("Token刷新成功: appId={}, accountName={}",
                config.getAppId(), config.getAccountName());
        } finally {
            redisUtil.del(lockKey);
        }
    }
    
    /**
     * 强制刷新指定账号的Token
     * 
     * @param appId 微信AppID
     * @return 新的 Access Token
     */
    public String forceRefreshToken(String appId) {
        String tokenKey = TOKEN_KEY_PREFIX + appId;
        String lockKey = LOCK_KEY_PREFIX + appId;
        
        boolean locked = redisUtil.set(lockKey, "1", LOCK_EXPIRE);
        if (!locked) {
            throw new RuntimeException("Token正在刷新中，请稍后重试: appId=" + appId);
        }
        
        try {
            // 删除旧Token
            redisUtil.del(tokenKey);
            
            // 获取配置并刷新
            WechatConfig config = getConfigByAppId(appId);
            String token = fetchTokenFromWechat(config.getAppId(), config.getAppSecret());
            redisUtil.set(tokenKey, token, TOKEN_EXPIRE);
            
            log.info("强制刷新Token成功: appId={}", appId);
            return token;
            
        } finally {
            redisUtil.del(lockKey);
        }
    }
    
    /**
     * 获取所有已缓存的Token状态（用于监控）
     * 
     * @return Token状态信息
     */
    public Map<String, Object> getAllTokenStatus() {
        List<WechatConfig> configs = configMapper.selectList(
            new QueryWrapper<WechatConfig>().eq("is_enabled", 1)
        );
        
        Map<String, Object> result = new HashMap<>();
        
        for (WechatConfig config : configs) {
            String tokenKey = TOKEN_KEY_PREFIX + config.getAppId();
            Object token = redisUtil.get(tokenKey);
            long ttl = redisUtil.getExpire(tokenKey);
            
            Map<String, Object> info = new HashMap<>();
            info.put("accountName", config.getAccountName());
            info.put("configType", config.getConfigType());
            info.put("hasToken", token != null);
            info.put("ttl", ttl);
            info.put("ttlMinutes", ttl > 0 ? ttl / 60 : 0);
            
            result.put(config.getAppId(), info);
        }
        
        return result;
    }
    
    /**
     * 从数据库获取配置
     * 
     * @param appId 微信AppID
     * @return 微信配置
     */
    private WechatConfig getConfigByAppId(String appId) {
        WechatConfig config = configMapper.selectOne(
            new QueryWrapper<WechatConfig>()
                .eq("app_id", appId)
                .eq("is_enabled", 1)
        );
        
        if (config == null) {
            throw new RuntimeException("AppID不存在或未启用: " + appId);
        }
        
        return config;
    }
    
    /**
     * 随机获取一个启用账号的 Token（负载均衡/随机选择）
     * 
     * @return Map包含appId和token
     */
    public Map<String, String> getRandomAccessToken() {
        // 1. 查询所有启用的配置
        List<WechatConfig> configs = configMapper.selectList(
            new QueryWrapper<WechatConfig>().eq("is_enabled", 1)
        );
        
        if (configs.isEmpty()) {
            throw new RuntimeException("没有启用的微信账号");
        }
        
        // 2. 随机选择一个账号
        Random random = new Random();
        WechatConfig selectedConfig = configs.get(random.nextInt(configs.size()));
        
        // 3. 获取该账号的Token
        String token = getAccessToken(selectedConfig.getAppId());
        
        log.info("随机获取Token: appId={}, accountName={}", 
            selectedConfig.getAppId(), selectedConfig.getAccountName());
        
        // 4. 返回结果
        Map<String, String> result = new HashMap<>();
        result.put("appId", selectedConfig.getAppId());
        result.put("thumbMediaId", selectedConfig.getThumbMediaId());
        result.put("configType", selectedConfig.getConfigType());
        result.put("email", selectedConfig.getEmail());
        result.put("token", token);
        
        return result;
    }
    
    /**
     * 根据配置类型随机获取一个账号的 Token
     * 
     * @param configType 配置类型（official_account/mini_program/work_wechat）
     * @return Map包含appId和token
     */
    public Map<String, String> getRandomAccessTokenByType(String configType) {
        // 1. 查询指定类型的所有启用配置
        List<WechatConfig> configs = configMapper.selectList(
            new QueryWrapper<WechatConfig>()
                .eq("is_enabled", 1)
                .eq("config_type", configType)
        );
        
        if (configs.isEmpty()) {
            throw new RuntimeException("没有启用的" + configType + "类型账号");
        }
        
        // 2. 随机选择一个账号
        Random random = new Random();
        WechatConfig selectedConfig = configs.get(random.nextInt(configs.size()));
        
        // 3. 获取该账号的Token
        String token = getAccessToken(selectedConfig.getAppId());
        
        log.info("随机获取{}类型Token: appId={}, accountName={}", 
            configType, selectedConfig.getAppId(), selectedConfig.getAccountName());
        
        // 4. 返回结果
        Map<String, String> result = new HashMap<>();
        result.put("appId", selectedConfig.getAppId());
        result.put("accountName", selectedConfig.getAccountName());
        result.put("configType", selectedConfig.getConfigType());
        result.put("token", token);
        
        return result;
    }
    
    /**
     * 从微信API获取Token
     * 
     * @param appId 微信AppID
     * @param appSecret 微信AppSecret
     * @return Access Token
     */
    private String fetchTokenFromWechat(String appId, String appSecret) {
        String url = String.format(
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
            appId, appSecret
        );
        
        try {
            log.debug("调用微信API获取Token: appId={}", appId);
            String response = restTemplate.getForObject(url, String.class);
            JSONObject json = JSON.parseObject(response);
            
            if (json.containsKey("access_token")) {
                return json.getString("access_token");
            } else {
                String errcode = json.getString("errcode");
                String errmsg = json.getString("errmsg");
                throw new RuntimeException(String.format(
                    "获取Token失败: appId=%s, errcode=%s, errmsg=%s", 
                    appId, errcode, errmsg
                ));
            }
        } catch (Exception e) {
            log.error("调用微信API异常: appId={}, error={}", appId, e.getMessage());
            throw new RuntimeException("调用微信API失败: " + e.getMessage(), e);
        }
    }
}
